package cn.signalpha.common.aspect;

import cn.signalpha.common.annotation.OptimisticLockRetryAnnotation;
import cn.signalpha.common.exception.OptimisticLockRetryException;
import cn.signalpha.core.log.logger.LoggerUtils;
import cn.signalpha.core.tool.jackson.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 乐观锁重试AOP
 *
 * @author signalpha
 */
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class OptimisticLockRetryAspect implements Ordered {

	private final LoggerUtils logger;

	@Pointcut("@annotation(cn.signalpha.common.annotation.OptimisticLockRetryAnnotation)")
	public void retryOnOptFailure() {}

	@Around("retryOnOptFailure()")
	public Object doConcurrentOperation(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		Signature signature = proceedingJoinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method targetMethod = methodSignature.getMethod();
		OptimisticLockRetryAnnotation annotation = targetMethod.getAnnotation(OptimisticLockRetryAnnotation.class);
		Integer retryTimes = annotation.retryTimes();
		while (retryTimes > 0) {
			retryTimes--;
			try {
				return proceedingJoinPoint.proceed();
			} catch (OptimisticLockRetryException e) {
				log.error("{}乐观锁重试失败，剩余次数：{}", targetMethod, retryTimes);
			}
		}
		logger.error("optimisticLockRetry", getFieldsName(proceedingJoinPoint));
		return 0;
	}

	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE;
	}

	/**
	 * 获取参数列表
	 */
	private String getFieldsName(ProceedingJoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		String[] parameterNames = pnd.getParameterNames(method);
		Map<String, Object> paramMap = new HashMap<>(32);
		for (int i = 0; i < Objects.requireNonNull(parameterNames).length; i++) {
			paramMap.put(parameterNames[i], args[i]);
		}
		return JsonUtil.toJson(paramMap);
	}
}
